package com.androidtest3d.scene;

/**
 * Android Game Tutorial Project.
 *
 * Copyright (C) 2011  Philipp Jenke
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import java.util.ArrayList;
import java.util.List;

import android.util.Log;

import com.androidtest3d.math.DimensionsNotMatchException;
import com.androidtest3d.math.MyVector;
import com.androidtest3d.math.Triangle;
import com.androidtest3d.math.Vertex;

import javax.microedition.khronos.opengles.GL10;

/**
 * Represents a 3D cube.
 * 
 */
public class TriangleMesh extends Node {

	/**
	 * This identifier is used in the log.
	 */
	private static final String TAG = "TriangleMesh";

	/**
	 * List of vertices.
	 */
	private List<Vertex> vertices = new ArrayList<Vertex>();

	/**
	 * List of triangles.
	 */
	private List<Triangle> triangles = new ArrayList<Triangle>();

	/**
	 * Temporary data structures containing the vertex information.
	 */
	private FloatBuffer vertexBuffer = null;

	/**
	 * Temporary data structures containing the normal information.
	 */
	private FloatBuffer normalBuffer = null;

	/**
	 * Temporary data structures containing the index information.
	 */
	private ShortBuffer indexBuffer = null;

	/**
	 * Init structures.
	 */
	public void init() {

		// Create a dummy mesh
		try {
			createCube(vertices, triangles);
		} catch (DimensionsNotMatchException e) {
			Log.e(TAG, e.getMessage());
		}

		// Create vertex buffer
		{
			float vb[] = new float[vertices.size() * 3];
			for (int i = 0; i < vertices.size(); i++) {
				vb[i * 3] = (float) vertices.get(i).getCoords().get(0);
				vb[i * 3 + 1] = (float) vertices.get(i).getCoords().get(1);
				vb[i * 3 + 2] = (float) vertices.get(i).getCoords().get(2);
			}
			final int floatsize = 4;
			final int spacePerVertex = 3 * floatsize;
			ByteBuffer vbb = ByteBuffer.allocateDirect(spacePerVertex
					* vertices.size());
			vbb.order(ByteOrder.nativeOrder());
			vertexBuffer = vbb.asFloatBuffer();
			vertexBuffer.put(vb);
			vertexBuffer.position(0);
		}

		// Create normal buffer
		{
			float nb[] = new float[vertices.size() * 3];
			for (int i = 0; i < vertices.size(); i++) {
				nb[i * 3] = (float) vertices.get(i).getNormal().get(0);
				nb[i * 3 + 1] = (float) vertices.get(i).getNormal().get(1);
				nb[i * 3 + 2] = (float) vertices.get(i).getNormal().get(2);
			}
			final int floatsize = 4;
			final int spacePerNormal = 3 * floatsize;
			ByteBuffer nbb = ByteBuffer.allocateDirect(spacePerNormal
					* vertices.size());
			nbb.order(ByteOrder.nativeOrder());
			normalBuffer = nbb.asFloatBuffer();
			normalBuffer.put(nb);
			normalBuffer.position(0);
		}

		// Create index buffer
		{
			short ib[] = new short[triangles.size() * 3];
			for (int i = 0; i < triangles.size(); i++) {
				ib[i * 3] = (short) triangles.get(i).getA();
				ib[i * 3 + 1] = (short) triangles.get(i).getB();
				ib[i * 3 + 2] = (short) triangles.get(i).getC();
			}
			final int shortSize = 2;
			final int spacePerTriangle = shortSize * 3;
			ByteBuffer ibb = ByteBuffer.allocateDirect(triangles.size()
					* spacePerTriangle);
			ibb.order(ByteOrder.nativeOrder());
			indexBuffer = ibb.asShortBuffer();
			indexBuffer.put(ib);
			indexBuffer.position(0);
		}
	}

	/**
	 * Fill the required arrays for a cube.
	 * 
	 * @param vertices
	 * @param triangles
	 * @throws Exception
	 */
	private void createCube(List<Vertex> vertices, List<Triangle> triangles)
			throws DimensionsNotMatchException {

		final float size = 0.5f;

		vertices.clear();
		triangles.clear();

		// Vertices
		vertices.add(new Vertex(MyVector.makeVector3(-size, -size, -size),
				MyVector.makeVector3(-size, -size, -size), MyVector
						.makeVector3(0.25, 0.75, 0.25))); // v0
		vertices.add(new Vertex(MyVector.makeVector3(size, -size, -size),
				MyVector.makeVector3(-size, -size, -size), MyVector
						.makeVector3(0.25, 0.75, 0.25))); // v1
		vertices.add(new Vertex(MyVector.makeVector3(size, size, -size),
				MyVector.makeVector3(-size, -size, -size), MyVector
						.makeVector3(0.25, 0.75, 0.25))); // v2
		vertices.add(new Vertex(MyVector.makeVector3(-size, size, -size),
				MyVector.makeVector3(-size, -size, -size), MyVector
						.makeVector3(0.25, 0.75, 0.25))); // v3
		vertices.add(new Vertex(MyVector.makeVector3(-size, -size, size),
				MyVector.makeVector3(-size, -size, -size), MyVector
						.makeVector3(0.25, 0.75, 0.25))); // v4
		vertices.add(new Vertex(MyVector.makeVector3(size, -size, size),
				MyVector.makeVector3(-size, -size, -size), MyVector
						.makeVector3(0.25, 0.75, 0.25))); // v5
		vertices.add(new Vertex(MyVector.makeVector3(size, size, size),
				MyVector.makeVector3(-size, -size, -size), MyVector
						.makeVector3(0.25, 0.75, 0.25))); // v6
		vertices.add(new Vertex(MyVector.makeVector3(-size, size, size),
				MyVector.makeVector3(-size, -size, -size), MyVector
						.makeVector3(0.25, 0.75, 0.25))); // v7

		triangles.add(new Triangle(0, 1, 2));
		triangles.add(new Triangle(0, 2, 3));
		triangles.add(new Triangle(1, 5, 2));
		triangles.add(new Triangle(5, 6, 2));
		triangles.add(new Triangle(5, 4, 6));
		triangles.add(new Triangle(4, 7, 6));
		triangles.add(new Triangle(4, 0, 7));
		triangles.add(new Triangle(0, 3, 7));
		triangles.add(new Triangle(3, 2, 7));
		triangles.add(new Triangle(2, 6, 7));
		triangles.add(new Triangle(1, 0, 4));
		triangles.add(new Triangle(1, 4, 5));
	}

	/**
	 * Render mesh
	 */
	public void draw(GL10 gl) {
		// Call vertex list
		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);

		// Colors
		// gl.glColorPointer(3, GL10.GL_FLOAT, 3, colorBuffer);

		// Normals
		gl.glNormalPointer(GL10.GL_FLOAT, 0, normalBuffer);

		// Draw vertices via indices
		gl.glDrawElements(GL10.GL_TRIANGLES, triangles.size() * 3,
				GL10.GL_UNSIGNED_SHORT, indexBuffer);

		// Call super method in order to descent into children
		super.draw(gl);
	}
}
